#!/usr/bin/python3
# ******************************************************************************
# Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
# licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# Author: DisNight
# Create: 2021-06-24
# ******************************************************************************/

import os
import logging
import shutil

from javcra.libs.config.global_config import (
    CHECK_REQUIRES_CONFIG,
    DEFAULT_OSCRC_POSITION,
    FAILED,
    SUCCEED,
)
from javcra.application.checkpart.check_requires import init_env
from javcra.application.checkpart.check_requires.util.check_abi import CheckAbi
from javcra.application.checkpart.check_requires.util.dnf_utils import (
    find_update_install_rpms,
)
from javcra.application.checkpart.check_requires.util.file_utils import (
    save_simple_file,
    save_dict_file,
)
from javcra.application.checkpart.check_requires.download_rpms import (
    get_bin_rpmlist,
    download_required_rpm,
    download_all_pkgbin,
)
from javcra.application.checkpart.check_requires.find_whatrequires import (
    get_all_berequired,
)
from javcra.application.checkpart.check_requires.change_bin2src import (
    change_all_bin2src,
)


def get_whatrequires(
    pkg_list: list,
    published_repos: list,
    dev_repos: list,
    branch: str = "openEuler:20.03:LTS:SP1",
) -> dict:
    """
    get effected src rpm list which requires rpm in pkg_list

    Attributes:
        pkg_list: source rpm list
        published_repos: published repo name list in /etc/yum.repo.d/xxx.repo (repo.openuler.org/xxx etc.)
        dev_repos: develop repo name list in /etc/yum.repo.d/xxx.repo (obs.repo/xxxx etc.)
        branch: branch in obs proj such as (openEuler:20.03:LTS:SP1, openEuler:20.03:LTS:SP1:EPOL, etc.)

    return:
        pkg_dict = {
            src_rpm: [bin_rpm],
            ...
        }
    """
    pass


def get_update_install(
    pkg_list: list,
    published_repos: list,
    dev_repos: list,
    branch: str = "openEuler:20.03:LTS:SP1",
) -> dict:
    """
    get effected src rpm list which requires rpm in pkg_list

    Attributes:
        pkg_list: source rpm list
        published_repos: published repo name list in /etc/yum.repo.d/xxx.repo (repo.openuler.org/xxx etc.)
        dev_repos: develop repo name list in /etc/yum.repo.d/xxx.repo (obs.repo/xxxx etc.)
        branch: branch in obs proj such as (openEuler:20.03:LTS:SP1, openEuler:20.03:LTS:SP1:EPOL, etc.)

    return:
        pkg_dict = {
            src_rpm: [bin_rpm],
            ...
        }
    """
    pass


def find_all_effect_bin_rpms(
    save_dir, required_dir, src2bin, work_dir="/var/tmp", dirs=["published", "update"]
):
    """
    use CheckAbi to find all effected rpm by changed abi in <save_dir>/<pkg_name>/<dirs[x]>

    Attributes:
        save_dir: published rpm stored in <save_dir>/<pkg_name>/<dir[0]>
                  update rpm stored in <save_dir>/<pkg_name>/<dir[-1]>
        required_dir: required rpms strored here
        src2bin: {src_rpm_name : {bin : [bin_rpm_name_list]}}
        work_dir: CheckAbi result doc stored in here
        dirs: subdir of published rpm and update rpm

    return:
        effect_bin_rpms = [bin_rpm_name_list]
    """
    if not dirs or not isinstance(dirs, list):
        logging.error("illegal dirs type: %s", str(type(dirs)))
        return {}
    effect_bin_rpms = {}
    for pkg_name in src2bin:
        pre_dir = os.path.join(save_dir, pkg_name, dirs[0])
        ori_dir = os.path.join(save_dir, pkg_name, dirs[-1])
        paths = [pre_dir, ori_dir]
        check_abi_process = CheckAbi(work_dir, "", False, False, required_dir)
        check_abi_process.process_with_rpms(paths)
        logging.info(
            "pkg: %s find %d effect rpms",
            pkg_name,
            len(check_abi_process.effect_rpm_list),
        )
        effect_bin_rpms.update(
            {k: pkg_name for k in list(check_abi_process.effect_rpm_list)}
        )
    return effect_bin_rpms


def find_all_update_install_rpms(save_dir, sub_dir, src2bin, repos, repo_file):
    """
    get all rpm which need update together

    Attributes:
        save_dir:
        src2bin:
        repos: update repos provided by obs(open build service)

    return:
        update_bin_dict: {
            bin_name : effected_by_rpm_name
        }
    """
    update_bin_dict = set()
    for pkg_name in src2bin:
        work_dir = os.path.join(save_dir, pkg_name, sub_dir[-1])
        if not os.path.isdir(work_dir):
            logging.warning("illegal path for pkg: %s", pkg_name)
            continue
        update_bin_dict.update(
            {
                k: pkg_name
                for k in list(
                    find_update_install_rpms(pkg_name, work_dir, repos, repo_file)
                )
            }
        )
    return update_bin_dict


def find_all_updated(init_list, branch, repo_file=None, save_file=True):
    """

    result:
        {
            src_name : {
                bin : [bin_name_list],
                reason : abi_effected/update,
                effected_by : src_rpm_name
            }
        }
    """
    new_update_dict = {k: ["", ""] for k in init_list}
    all_pkg2bin = {}
    index = 1
    while new_update_dict:
        print("update_list: " + str(new_update_list))
        logging.info("find all update rpms round %d", index)
        index += 1
        now_dict = new_update_dict
        new_update_list = []
        # download bin rpms in obs and repo
        pkg2bin_map = get_bin_rpmlist(now_dict, obs_prj=branch.replace("-", ":"))
        download_all_pkgbin(
            CHECK_REQUIRES_CONFIG.UPDATE_RPM_SAVE_PATH,
            pkg2bin_map,
            CHECK_REQUIRES_CONFIG.PUBLISHED_REPO_LIST,
            CHECK_REQUIRES_CONFIG.UPDATE_REPO_LIST,
            CHECK_REQUIRES_CONFIG.UPDATE_RPM_SAVE_SUBPATH,
            repo_file,
        )
        # download installed
        now_required_rpms = get_all_berequired(
            CHECK_REQUIRES_CONFIG.UPDATE_RPM_SAVE_PATH,
            pkg2bin_map,
            CHECK_REQUIRES_CONFIG.UPDATE_RPM_SAVE_SUBPATH[-1],
            repo_file,
        )
        save_simple_file(
            CHECK_REQUIRES_CONFIG.UPDATE_RPM_SAVE_PATH,
            "required_list_{}".format(index - 1),
            now_required_rpms,
        )
        download_required_rpm(
            CHECK_REQUIRES_CONFIG.EFFECTED_RPM_SAVE_PATH,
            now_required_rpms,
            CHECK_REQUIRES_CONFIG.PUBLISHED_REPO_LIST,
            repo_file,
        )
        # find updated install requires
        all_pkg2bin.update(pkg2bin_map)
        if save_file:
            save_dict_file(
                CHECK_REQUIRES_CONFIG.UPDATE_RPM_SAVE_PATH, "pkg2bin.json", all_pkg2bin
            )
        update_install_rpms = find_all_update_install_rpms(
            CHECK_REQUIRES_CONFIG.UPDATE_RPM_SAVE_PATH,
            pkg2bin_map,
            CHECK_REQUIRES_CONFIG.UPDATE_REPO_LIST,
            repo_file,
        )
        # update all src2bin map, find  updated installed pkg
        update_effect_rpms = find_all_effect_bin_rpms(
            CHECK_REQUIRES_CONFIG.UPDATE_RPM_SAVE_PATH,
            CHECK_REQUIRES_CONFIG.EFFECTED_RPM_SAVE_PATH,
            pkg2bin_map,
        )
        all_pkg2bin.update(pkg2bin_map)
        update_src_dict = change_all_bin2src(update_effect_rpms, all_pkg2bin)
        temp_update_dict = {
            k: ["abi_effected", update_src_dict[k]] for k in update_src_dict
        }
        update_src_dict = change_all_bin2src(update_install_rpms, all_pkg2bin)
        temp_update_dict.update({
            k: ["update", update_src_dict[k]] for k in update_src_dict
        })
        new_update_dict = temp_update_dict
    logging.info("all finished")
    return all_pkg2bin


def prep_get_effected_rpms():
    """
    prepare the config the check part:
    create tmp dir; get yum repo file; init oscrc config

    Attribute:

    return:
        SUCCEED or FAILED
    """
    if (
        not CHECK_REQUIRES_CONFIG.OBS_USR_ACCOUNT
        or not CHECK_REQUIRES_CONFIG.OBS_USR_PASSWD
    ):
        logging.error("accout or passwd is empty")
        return FAILED
    if not init_env.init_oscrc(
        CHECK_REQUIRES_CONFIG.OBS_USR_ACCOUNT, CHECK_REQUIRES_CONFIG.OBS_USR_PASSWD
    ):
        logging.error("failed to init oscrc")
        return FAILED

    # create tmp dirctory
    def create_tmp_path(prefix, suffix_list):
        for suffix in suffix_list:
            tmp_path = os.path.join(prefix, suffix)
            _ = not os.path.exists(tmp_path) and os.makedirs(tmp_path)

    create_tmp_path(
        CHECK_REQUIRES_CONFIG.UPDATE_RPM_SAVE_PATH,
        CHECK_REQUIRES_CONFIG.UPDATE_RPM_SAVE_SUBPATH,
    )
    create_tmp_path(CHECK_REQUIRES_CONFIG.CHECK_ABI_WORK_DIR, [""])

    return SUCCEED


def clear_get_effected_rpms(skip_oscrc=False, skip_tmpdir=False):
    """
    clear file created by check_requires

    Attribute:
        skip_oscrc: skip clear "/root/.config/osc/oscrc"
        skip_tmpdir: skip clear "/tmp/release-tools/check_requires"

    return:
        None
    """
    if not skip_oscrc and os.path.exists(DEFAULT_OSCRC_POSITION):
        os.remove(DEFAULT_OSCRC_POSITION)
    if not skip_tmpdir and os.path.exists(
        CHECK_REQUIRES_CONFIG.CHECK_REQUIRES_WORK_DIR
    ):
        shutil.rmtree()
